package com.nage.components.movement;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Stack;

import android.util.Log;

import com.nage.components.Component;
import com.nage.engine.GameObject;
import com.nage.utils.ByteArrayBuilder;

public class Physics extends Component {
	
	Vector2D pos; // position of object
	public Vector2D vel; // velocity
	public Vector2D acc; // acceleration
	Stack<Vector2D> forces; // constant forces applied to object
	Stack<Vector2D> impulses; // instantanious forces (impulses) applied to object.
	public float mass; // mass of object

	public Vector2D resultantForce;
	
	Vector2D drag;
	float dragCoef;
		
	// An object which have a Physics component will always have an x and y
	// velocity to measure how
	// fast it is moving in a direction, the x and y acceleration, to measure
	// how the velocity is changing
	// and a mass and bounce coefficient for collision calculations.
	// xVelocity, yVelocity: a value in pixels/ms. This can be used to
	// recalculate x and y position by
	// multiplying the xVelocity by the delta time and adding that to the
	// current positions.
	// xAcceleration, yAcceleration: values in pixels/ms^2 - ie. how much the
	// xVelocity changes every ms.

	public Physics(GameObject p) {
		super(p);
		type = PHYSICS;
		
		pos = new Vector2D(parent.x, parent.y);
		vel = new Vector2D(0, 0);
		acc = new Vector2D(0, 0);
		forces = new Stack<Vector2D>();
		impulses = new Stack<Vector2D>();
		mass = 1; // default mass for now.
		
		resultantForce = new Vector2D(0, 0);
		
		drag = new Vector2D(0, 0);
		dragCoef = -0.5f;
						
	}
	
	public void addForce(Vector2D f) {
		forces.push(f);
	}
	public void addForce(float x, float y) {
		addForce(new Vector2D(x, y));		
	}
	public void removeForce() {
		forces.remove(forces.size()-1);
	}
	public void addImpulse(Vector2D i) {
		impulses.push(i);
	}
	public void addImpulse(float f, float g) {
		addImpulse(new Vector2D(f, g));
	}

	@Override
	public void update(long delta) {
				
		pos.x = parent.x;
		pos.y = parent.y;
		
		float time = (float)delta / 1000.0f;
		
		if(mass==0.0f) {
			return;
		}
		
		resultantForce.reset(); // the force being applied to object during this update.
		
		// Add each of the constant forces to the total force for this update
		for(Vector2D f : forces) {
			resultantForce.add(f);
		}
		
		// Add each of the instantanious forces to the total force for this update,
		// removing them as they are added.
		while(!impulses.empty()) {
			resultantForce.add(impulses.pop());
		}
		
		
		// update physics state using the forces applied to object
		acc = resultantForce.divide(mass); // a = f / m
		vel.add(acc.x * time, acc.y * time); // v += a * t
		pos.add(vel.x * time, vel.y * time); // p += v * t
		
		// drag = (vel * dragCoef) / mass
		drag = (vel.multiply(dragCoef)).divide(mass);
		addImpulse(drag);
		
		//printDetails();

		// propogate values to object.
		parent.setX(pos.x);
		parent.setY(pos.y);
		
		modified = true;
			
	}

	public void stop() {
		vel.set(0, 0);
		acc.set(0, 0);
		drag.set(0, 0);
		forces.removeAllElements();
		impulses.removeAllElements();
		
	}

	
	public void printDetails() {
		Log.d("Physics", "			force = "+resultantForce.toString());
		Log.d("Physics", "			acc   = "+acc.toString());
		Log.d("Physics", "			vel   = "+vel.toString());
		Log.d("Physics", "			pos   = "+pos.toString());
		Log.d("Physics", "			drag  = "+drag.toString());
		Log.d("Physics", "			");
	}

	public void setMass(String m) {
		if(m!=null) {
			if(m.equals("infinity")) {
				mass=Float.POSITIVE_INFINITY;
			} else {
				mass = Float.parseFloat(m);
			}
		}
	}

	public boolean flatten(ByteArrayOutputStream stream_builder) throws IOException {
		stream_builder.write(type);
		stream_builder.write(id);
		stream_builder.write(ByteArrayBuilder.floatToByteArray(pos.x));
		stream_builder.write(ByteArrayBuilder.floatToByteArray(pos.y));
		stream_builder.write(ByteArrayBuilder.floatToByteArray(vel.x));
		stream_builder.write(ByteArrayBuilder.floatToByteArray(vel.y));
		stream_builder.write(ByteArrayBuilder.floatToByteArray(acc.x));
		stream_builder.write(ByteArrayBuilder.floatToByteArray(acc.y));

		return true;
	}
	
	public int expand(byte[] b, int i) {
		id = b[i++];
		pos.set(ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]), 
				ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]));
		vel.set(ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]), 
				ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]));
		acc.set(ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]), 
				ByteArrayBuilder.byteArrayToFloat(b[i++], b[i++], b[i++], b[i++]));
		
	//	printDetails();
		return i;
	}



}
